package base

// 关于数据连接的一些考虑
// 一个应用在启动的时候，需要连接到哪些数据库肯定是已知的，如果是多库的情况，应该为每个库都维护一个自己相关的连接
// data层不应该由biz层直接传入，而且data层的操作也是一样，应该具体且明确的表示
import (
	"time"
	"vmapp/app0/internal/biz/repo"
	"vmapp/app0/internal/conf"
	"vmapp/app0/internal/models"
	"vmapp/pkg/encrypt/rand"
	"vmapp/pkg/encrypt/sha"

	"github.com/aveyuan/vbasedata"
	"github.com/aveyuan/vrbac"

	redis "github.com/redis/go-redis/v9"
	"gorm.io/gorm"
)

type (
	contextTxKey struct{}
)

type Data struct {
	Mysql   *gorm.DB
	Redis   redis.UniversalClient
	Rbac    *vrbac.VRbac
	cleanup []func()
}

// NewTransaction 实现接口TX
func NewTransaction(d *Data) repo.Transaction {
	return d
}

// NewData 初始化数据
func NewData(c *conf.AppConf, bc *conf.BootComponent) (*Data, func(), error) {
	data := &Data{}
	// 程序退出后，资源释放
	cleanup := func() {
		bc.Logger.Info("清理连接资源")
		for _, v := range data.cleanup {
			v()
		}
	}
	if c.Data == nil {
		return data, cleanup, nil
	}

	if c.Data.DB != nil {
		g, f, err := vbasedata.NewGorm(c.Data.DB, bc.Logger)
		if err != nil {
			panic(err)
		}
		data.cleanup = append(data.cleanup, f)
		data.Mysql = g
		g.AutoMigrate(
			new(models.User),
			new(models.Role),
			new(models.Menu),
			new(models.Send),
			new(models.File),
			new(models.EventLog),
			new(models.Option),
		)

		var uc int64
		g.Model(new(models.User)).Count(&uc)
		if uc == 0 {
			bc.Logger.Info("初始化默认用户")
			salt := rand.RandStr(8)

			one := &models.User{
				Uid:       bc.Idgenerator.NextId(),
				Username:  "admin",
				IsAdmin:   1,
				Salt:      salt,
				Password:  sha.Sha256("12345678" + salt),
				LastLogin: time.Now(),
				CreatedAt: time.Now(),
				UpdatedAt: time.Now(),
				State:     1,
				UserRoles: "",
			}

			if err := g.Create(one).Error; err != nil {
				bc.Logger.Errorf("创建用户数据失败,%v", err)
				return nil, cleanup, err
			}
		}

		// 载入模型
		bc.Logger.Info("初始化权限系统模型")
		rbac := vrbac.NewVRbac(g)
		if err := rbac.Init(); err != nil {
			bc.Logger.Infof("初始化权限系统模型失败,%v", err)
			panic(err)
		}
		bc.Logger.Info("载入权限系统模型")
		if err := rbac.LoadPolicy(); err != nil {
			bc.Logger.Infof("载入权限系统模型,%v", err)
			panic(err)
		}
		data.Rbac = rbac
	}

	if c.Data.Redis != nil {
		r, f, err := vbasedata.NewRedis(c.Data.Redis, bc.Logger)
		if err != nil {
			bc.Logger.Errorf("Redis 连接失败,%v", err)
			panic(err)
		}
		data.cleanup = append(data.cleanup, f)
		data.Redis = r
	}

	return data, cleanup, nil
}
